

#include "UHY_LinearSVM.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

/**
  * @brief								Load SVM model
  * @param			[in]			model_file_name - model name
  * @return							SVM model
  */
struct model *ReadSVMModel(
	const char *iptFileName )
{
	// Open SVM model
	FILE *fp = fopen (iptFileName, "r" );
	// The weight length
	int w_length;
	// The number of feature
	int feaNum;
	// The number of class
	int classNum;
	// The bias
	double bias;

	// Memory allocation
	model *pModel = UHY_MALLOC( model, 1 );
	parameter& pParam = pModel->param;

	// Set the label
	pModel->label = NULL;

	// Temp string for reading the file
	char str[81];

	// Read the model
	while(1)
	{
		fscanf( fp, "%80s", str );

		// Get the solve type
		if ( strcmp( str, "solver_type" ) == 0 )
		{
			fscanf( fp, "%80s", str );
			// Solve type
			int solveidx = 0;
			// Iteration for the solve type
			for( solveidx; svm_solvetype[solveidx]; solveidx++ )
			{
				// Set the solve type
				if ( strcmp( svm_solvetype[solveidx], str ) == 0 )
				{
					pParam.solver_type = solveidx;
					break;
				}
			}
		}
		// Model bias
		else if( strcmp( str, "bias" ) == 0 )
		{
			fscanf( fp, "%lf", &bias );
			pModel->bias = bias;
		}
		// Class number
		else if( strcmp( str, "nr_class" ) == 0 )
		{
			fscanf( fp, "%d", &classNum );
			pModel->nr_class = classNum;
		}
		// Label
		else if ( strcmp( str, "label" ) == 0 )
		{
			pModel->label = UHY_MALLOC( int, pModel->nr_class );
			for( int i = 0; i < pModel->nr_class; i++ )
			{
				fscanf( fp, "%d", &pModel->label[i] );
			}
		}
		// Feature number
		else if( strcmp( str, "nr_feature" ) == 0 )
		{
			fscanf( fp, "%d", &feaNum );
			pModel->nr_feature = feaNum;
		}
		// Weight
		else if( strcmp( str, "w" ) == 0 )
		{
			break;
		}
	}

	feaNum = pModel->nr_feature;
	// Solve the model bias
	w_length = ( pModel->bias >= 0 ) ? ( feaNum + 1 ) : ( feaNum );
	// number of weight
	int weightNum = ( classNum == 2 && pParam.solver_type != MCSVM_CS ) ? ( 1 ) : (classNum);

	// Allocation of the weight
	pModel->w = UHY_MALLOC( double, w_length * weightNum );
	for( int i = 0, idx = 0; i < w_length; i++ )
	{
		// Load the weights
		for( int k = 0; k < weightNum; k++, idx++ )
		{
			fscanf( fp, "%lf ", &pModel->w[idx] );
		}
		fscanf(fp, "\n");
	}

	fclose(fp);
	return pModel;
}

/**
  * @brief								SVM predict value output
  * @param			[in]			model_ - input model
  * @param			[in]			x - input vector
  * @param			[inout]		dec_values - output 
  * @return							label
  */
float predict_values_new(
	const struct model *model_,
	float *x,
	float *dec_values )
{
	int idx;
	int n;

	// Deal with the bias
	if ( model_->bias>=0 )
	{
		n = model_->nr_feature+1;
	}
	else
	{
		n = model_->nr_feature;
	}

	// Weight
	double *w = model_->w;

	// Class number
	int nr_class = model_->nr_class;
	int i;
	int nr_w;

	// Number of weight
	if ( nr_class == 2 && model_->param.solver_type != MCSVM_CS )
	{
		nr_w = 1;
	}
	else
	{
		nr_w = nr_class;
	}

	// Initial the decision value
	for( i = 0; i < nr_w; i++ )
	{
		dec_values[i]  = 0 ;
	}

	int nr_feature = model_->nr_feature;

	for( idx = 0; idx < n; idx++ )
	{
		// the dimension of testing data may exceed that of training
		for( i = 0; i < nr_w; i++ )
		{
			dec_values[i] += ( float )( w[idx * nr_w + i] * x[idx] );
		}
	}

	// Two classes
	if( nr_class == 2 )
	{
		if( model_->param.solver_type == L2R_L2LOSS_SVR ||
			model_->param.solver_type == L2R_L1LOSS_SVR_DUAL ||
			model_->param.solver_type == L2R_L2LOSS_SVR_DUAL)
		{
			// Return the label in the decision value
			return ( int )( dec_values[0] );
		}
		else
		{
			return ( dec_values[0] > 0 ) ? model_->label[0] : model_->label[1];
		}
	}
	// Multiple classes
	else
	{
		int dec_max_idx = 0;
		for ( i = 1; i < nr_class; i++ )
		{
			if( dec_values[i] > dec_values[dec_max_idx] )
			{
				dec_max_idx = i;
			}
		}
		return model_->label[dec_max_idx];
	}
}

/**
  * @brief								SVM predict value output
  * @param			[in]			model_ - input model
  * @param			[in]			x - input vector
  * @param			[inout]		prob_estimates - output 
  * @return							label
  */
float  predict_probability_new(
	const struct model *model_, 
	float *x, 
	float* prob_estimates )
{
	int i;
	int nr_class = model_->nr_class;
	int nr_w;

	// Number of weight
	if ( nr_class == 2 )
	{
		nr_w = 1;
	}
	else
	{
		nr_w = nr_class;
	}

	// label prediction
	float label = predict_values_new( model_, x, prob_estimates );

	// Calculate the probability
	for( i = 0; i < nr_w; i++ )
	{
		prob_estimates[i] = 1 / ( 1 + exp( -prob_estimates[i] ) );
	}

	// Two classes
	if ( nr_class == 2 ) 
	{
		prob_estimates[1] = 1.0f - prob_estimates[0];
	}
	// Multiple classes
	else
	{
		double sum = 0;
		for ( i = 0; i < nr_class; i++ )
		{
			sum += prob_estimates[i];
		}

		for( i = 0; i < nr_class; i++ )
		{
			prob_estimates[i] = (float)(prob_estimates[i] / sum);
		}
	}

	return label;	
}


/**
  * @brief								SVM predict value output
  * @param			[in]			FirPreVal - input vector1
  * @param			[in]			SecPreVal - input vector2
  * @param			[in]			FirIdx - input index 1 
  * @param			[in]			SecIdx - input index 2 
  * @param			[in]			nClass - class number
  * @return							output label
  */
int MulClassfierJudgeExp(float *ProVal)
{
	int JudeIdx = 0;
	int NextIdx = -1;
	float NextMaxVal = -1;
	float MaxVal = -1;
	for( int i = 0; i < 4; i++ )
	{
		float temp = ProVal[i];
		if( temp > NextMaxVal )
		{
			if ( temp > MaxVal )
			{
				NextMaxVal = MaxVal;
				NextIdx = JudeIdx;
				MaxVal = temp;
				JudeIdx = i;
			}
			else
			{
				NextMaxVal = temp;
				NextIdx = i;
			}
		}
	}

	// This part is only for making the video performance more stable
	/*if ( (JudeIdx == 0) && ( NextIdx == 2 ) )
	{
	if ( MaxVal < ( NextMaxVal + 1.2 ) )
	{
	JudeIdx = NextIdx;
	}
	}

	if ( (JudeIdx == 2) && ( NextIdx == 1 ) )
	{
	if ( MaxVal < ( NextMaxVal + 0.8 ) )
	{
	JudeIdx = NextIdx;
	}
	}*/
	return JudeIdx;
}


/**
  * @brief								Release SVM model
  * @param			[in]			model_ptr - model 
  * @return							SVM model
  */
void free_model_content(
	struct model *model_ptr )
{
	if ( model_ptr->w != NULL )
	{
		free( model_ptr->w );
	}
	if ( model_ptr->label != NULL )
	{
		free( model_ptr->label );
	}
}

/**
  * @brief								Release SVM model (whole structure)
  * @param			[in]			model_ptr_ptr - model name
  * @return							SVM model
  */
void free_and_destroy_model(
	struct model **model_ptr_ptr )
{
	struct model *model_ptr = *model_ptr_ptr;
	if ( model_ptr != NULL )
	{
		free_model_content( model_ptr );
		free( model_ptr );
	}
}
